Skip to main content

Extending the Template Package

In order to ensure that Template Packages could specify as wide a range of Twinit applications as possible, and to allow developers to work beyond the current support of the manifest.json file, the template package specification has extensibility built in.

For instance, if you wanted to create a Template Package that would create Item Service collections during deployment, you;d have no way o do so currently, as the manifest specification does not support it. Or if you wish to create more complex permissions.

In order to ensure that developers could do those things and more, the template package and deployment gives you the capability for:

A complete example of a custom deployment is available at the end of this page.

Including Custom Files in the Template Package

You can include any custom files in your template package in a custom folder. In the example below we have included a collections.json file in the template package.

Template Package.zip /
|
|-- configs /
|-- omapiConfig /
|-- scripts /
|
|-- custom /
| |-- collections.json
|
|-- manifest.json
|-- README.md

You can add as may files and folders as you like in the custom folder.

Why a 'custom' Folder?

While the package file needs to be extensible, it also needs to expect future enhancements and improvements. In our collctions example, if you create a collections folder at the root and included your custom collections implementation in it, that folder and its contents could collide in the future with a manifest enhancement to handle collection deployment.

By using a custom folder, you ensure your custom implementations continue to function even when the manifest and deployment process change.

Including a Custom Initialization and Setup Script

A custom initialization and setup script can also be included to run any deployment logic you would like, before and after the manifest based deploy has completed. Both are optional, and you need not use both or either.

  • The initialization script will run immediately at the start of the template package deployment before any other deployment tasks. You can use it to perform any pre-deployment configuration, updates, migration, or clean up.
  • The setup script runs last, after all other deployment tasks have finished. You can use it to complete any other setup needed, and is very useful for accomplishing tasks that the template package does not yet support or complex tasks like creating permissions.

You can configure an initialization and setup script by providing the paths to them in the manifest.

// manifest.json
{
"Template Name": "Water Treatment Operations Digital Twin",
"Template Version": "1.0.5",
"initializeScript": "custom/initScript.mjs",
"setupScript": "custom/setupMyTemplate.mjs"
}
Template Package.zip /
|
|-- configs /
|-- omapiConfig /
|-- scripts /
|
|-- custom /
| |-- collections.json
| |-- initScript.mjs
| |-- setupMyTemplate.mjs
|
|-- manifest.json
|-- README.md

The Initialization Script

Your custom initialize script must be in a file with the extension .mjs and it must export a function named init.

// initScript.mjs
export async function init(input, libraries, ctx, callback) {
// my initialization script content
}

The Setup Script

Your custom setup script must be in a file with the extension .mjs and it must export a function named setup.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {
// my setup script content
}

The Script Parameters

Both scripts are passed four parameters:

  • input
  • libraries
  • ctx
  • callback

The input Parameter

The input parameter provides access to:

  • your manifest
  • the complete template package contents
  • the project in which the deploy is happening
// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input

// my setup script content
}

Using the manifest parameter you can access any configuration in manifest.json.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input

console.log(manifest['Template Version']) // 1.0.5
console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs

// my setup script content
}

Using packageData parameter you can access to the contents of the Template Package zip file and the contents of the files in it. The Template Package mechanism utilizes JSZip for access to the zip contents. You have full access to the JSZip API through the packageData object.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input

console.log(manifest['Template Version']) // 1.0.5
console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs

// contents of custom/collections.json as a string
const collectionsFileContents = await packageData.file('custom/collections.json').async("string")
// contents of custom/collections.json as parsed JSON
const collectonsJSON = JSON.parse(collectionsFileContents)

// my setup script content
}

Using the project parameter you have access to the information for the project into which you are deploying.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input

console.log(manifest['Template Version']) // 1.0.5
console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs

// contents of custom/collections.json as a string
const collectionsFileContents = await packageData.file('custom/collections.json').async("string")
// contents of custom/collections.json as parsed JSON
const collectonsJSON = JSON.parse(collectionsFileContents)

console.log(project._name) // MyNewAppProject

// my setup script content
}

The libraries Parameter

The libraries parameter provides access to the Twinit SDK JavaScript libraries.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { PlatformApi, IafScriptEngine } = libraries
const { IafItemSvc, IafDataSource, IafPassSvc } = PlatformApi

// my setup script content
}

The ctx Parameter

The ctx parameter is the user's current context who is running the deploy.

This is the ctx passed to the Twinit JavaScript SDK methods.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

const { PlatformApi, IafScriptEngine } = libraries
const { IafPassSvc } = PlatformApi

const user = await IafPassSvc.getCurrentUser(ctx)
console.log(user.firstname) // John
console.log(user.lastname) // Doe

// my setup script content
}

The callback Parameter

The callback parameter allows you send string messages back to the deployment process to be included in the final deployment logs.

Always check that callback exists before using.

// setupMyTemplate.mjs
export async function setup(input, libraries, ctx, callback) {

if (callback) callback('INFO: Beginning custom setup script execution!')

// my setup script content

if (callback) callback('INFO: Completed custom setup script execution!')
}

A Complete Example

Template Package.zip /
|
|-- configs /
|-- omapiConfig /
|-- scripts /
|
|-- custom /
| |-- collections.json
| |-- initScript.mjs
| |-- setupMyTemplate.mjs
|
|-- manifest.json
|-- README.md
// manifest.json
{
"Template Name": "Water Treatment Operations Digital Twin",
"Template Version": "1.0.5",
"initializeScript": "custom/initScript.mjs",
"setupScript": "custom/setupMyTemplate.mjs"
}
// gets the current user from Twinit and sends a
// message to the deployment results
export async function init(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input
const { IafPassSvc } = libraries.PlatformApi

callback('INFO: Running initialize Script!')

const user = await IafPassSvc.getCurrentUser(ctx)

callback(`INFO: Current user is ${user._firstname} ${user._lastname}`)
}

// setupMyTemplate.mjs
// reads collections.json from the zip file and uses the contents
// to create NamedUserCollections in the project
export async function setup(input, libraries, ctx, callback) {

const { manifest, packageData, project } = input
const { IafItemSvc } = libraries.PlatformApi

function sendCallback(message) {
if (callback) callback(message)
}

// example mesage callback to results
sendCallback(`INFO: In script callback for project ${project._name}`)

// example of accesing data on the template manifest
sendCallback(`INFO: script run from manifest for ${manifest['Template Name']} version ${manifest['Template Version']}`)

// example of using manifest data to access and read json data from the template package
let collectionsString = await packageData.file('custom/collections.json').async("string")
let collections = JSON.parse(collectionsString)

//example of using PlatformAPI to configure the project via this script
if (collections && collections.length) {
for (let coll of collections) {
coll._namespaces = project._namespaces

try {
let newCollections = await IafItemSvc.createNamedUserItems(collections, 'NamedUserCollection', ctx)
sendCallback(`INFO: These collections created: ${newCollections._list.map(c => c._name).join(', ')}`)
} catch(error) {
sendCallback("ERROR: error creating collections in setup script, check console log for details")
console.error("ERROR: error creating collections in setup script")
console.error(error)
}

}
}

}